Εξερευνήστε τα WebGL occlusion queries για βελτιστοποιημένη απόδοση. Μάθετε πώς να τα χρησιμοποιείτε αποτελεσματικά για έλεγχο ορατότητας και σημαντικές βελτιώσεις απόδοσης στις web εφαρμογές σας.
WebGL Occlusion Queries: Έλεγχος Ορατότητας και Βελτιστοποίηση Απόδοσης
Στον κόσμο της ανάπτυξης WebGL, η απόδοση είναι υψίστης σημασίας. Οι πολύπλοκες σκηνές με πολυάριθμα αντικείμενα μπορούν γρήγορα να επιβαρύνουν την GPU, οδηγώντας σε πτώση καρέ και κακή εμπειρία χρήστη. Μια ισχυρή τεχνική για την αντιμετώπιση αυτού είναι το occlusion culling (απόκρυψη λόγω επικάλυψης), όπου τα αντικείμενα που είναι κρυμμένα πίσω από άλλα δεν αποδίδονται, εξοικονομώντας πολύτιμο χρόνο επεξεργασίας. Τα WebGL occlusion queries παρέχουν έναν μηχανισμό για τον αποτελεσματικό προσδιορισμό της ορατότητας των αντικειμένων, επιτρέποντας το αποτελεσματικό occlusion culling.
Τι είναι τα WebGL Occlusion Queries;
Ένα WebGL occlusion query είναι μια λειτουργία που σας επιτρέπει να ρωτήσετε την GPU πόσα fragments (pixels) σχεδιάστηκαν από ένα συγκεκριμένο σύνολο εντολών απόδοσης. Στην ουσία, υποβάλλετε κλήσεις σχεδίασης (draw calls) για ένα αντικείμενο, και η GPU σας λέει αν κάποιο από τα fragments του πέρασε τον έλεγχο βάθους (depth test) και ήταν πράγματι ορατό. Αυτή η πληροφορία μπορεί στη συνέχεια να χρησιμοποιηθεί για να προσδιοριστεί αν το αντικείμενο αποκρύπτεται από άλλα αντικείμενα στη σκηνή. Αν το query επιστρέψει μηδέν (ή έναν πολύ μικρό αριθμό), σημαίνει ότι το αντικείμενο ήταν εντελώς (ή σχεδόν εντελώς) αποκρυμμένο και δεν χρειάζεται να αποδοθεί στα επόμενα καρέ. Αυτή η τεχνική μειώνει σημαντικά το φόρτο εργασίας απόδοσης και βελτιώνει την απόδοση, ιδιαίτερα σε πολύπλοκες σκηνές.
Πώς Λειτουργούν τα Occlusion Queries: Μια Απλοποιημένη Επισκόπηση
- Δημιουργία Αντικειμένου Query: Πρώτα δημιουργείτε ένα αντικείμενο query χρησιμοποιώντας το
gl.createQuery(). Αυτό το αντικείμενο θα κρατήσει τα αποτελέσματα του occlusion query. - Έναρξη του Query: Ξεκινάτε το query χρησιμοποιώντας το
gl.beginQuery(gl.ANY_SAMPLES_PASSED, query). Ο στόχοςgl.ANY_SAMPLES_PASSEDκαθορίζει ότι μας ενδιαφέρει αν κάποια δείγματα (fragments) πέρασαν τον έλεγχο βάθους. Υπάρχουν και άλλοι στόχοι, όπως οgl.ANY_SAMPLES_PASSED_CONSERVATIVE(που παρέχει ένα πιο συντηρητικό αποτέλεσμα, περιλαμβάνοντας πιθανώς ψευδώς θετικά αποτελέσματα για καλύτερη απόδοση) και οgl.SAMPLES_PASSED(που μετρά τον αριθμό των δειγμάτων που πέρασαν τον έλεγχο βάθους, και είναι παρωχημένος στο WebGL2). - Απόδοση του Πιθανώς Αποκρυμμένου Αντικειμένου: Στη συνέχεια, εκδίδετε τις κλήσεις σχεδίασης για το αντικείμενο του οποίου την ορατότητα θέλετε να ελέγξετε. Συνήθως πρόκειται για ένα απλοποιημένο bounding box ή μια πρόχειρη αναπαράσταση του αντικειμένου. Η απόδοση μιας απλοποιημένης έκδοσης μειώνει την επίδραση του ίδιου του query στην απόδοση.
- Τερματισμός του Query: Τερματίζετε το query χρησιμοποιώντας το
gl.endQuery(gl.ANY_SAMPLES_PASSED). - Λήψη του Αποτελέσματος του Query: Το αποτέλεσμα του query δεν είναι άμεσα διαθέσιμο. Η GPU χρειάζεται χρόνο για να επεξεργαστεί τις εντολές απόδοσης και να καθορίσει τον αριθμό των fragments που πέρασαν. Μπορείτε να λάβετε το αποτέλεσμα χρησιμοποιώντας το
gl.getQueryParameter(query, gl.QUERY_RESULT). - Ερμηνεία του Αποτελέσματος: Αν το αποτέλεσμα του query είναι μεγαλύτερο από μηδέν, σημαίνει ότι τουλάχιστον ένα fragment του αντικειμένου ήταν ορατό. Αν το αποτέλεσμα είναι μηδέν, σημαίνει ότι το αντικείμενο ήταν εντελώς αποκρυμμένο.
- Χρήση του Αποτελέσματος για Occlusion Culling: Βάσει του αποτελέσματος του query, μπορείτε να αποφασίσετε αν θα αποδώσετε το πλήρες, λεπτομερές αντικείμενο στα επόμενα καρέ.
Οφέλη από τη Χρήση των Occlusion Queries
- Βελτιωμένη Απόδοση Απόδοσης: Αποφεύγοντας την απόδοση των αποκρυμμένων αντικειμένων, τα occlusion queries μπορούν να μειώσουν σημαντικά τον φόρτο εργασίας απόδοσης, οδηγώντας σε υψηλότερα καρέ ανά δευτερόλεπτο και μια πιο ομαλή εμπειρία χρήστη.
- Μειωμένος Φόρτος της GPU: Λιγότερη απόδοση σημαίνει λιγότερη δουλειά για την GPU, γεγονός που μπορεί να βελτιώσει τη διάρκεια ζωής της μπαταρίας σε φορητές συσκευές και να μειώσει την παραγωγή θερμότητας σε επιτραπέζιους υπολογιστές.
- Ενισχυμένη Οπτική Πιστότητα: Βελτιστοποιώντας την απόδοση, μπορείτε να έχετε την πολυτέλεια να αποδίδετε πιο πολύπλοκες σκηνές με μεγαλύτερη λεπτομέρεια χωρίς να θυσιάζετε το frame rate.
- Επεκτασιμότητα: Τα occlusion queries είναι ιδιαίτερα επωφελή για πολύπλοκες σκηνές με μεγάλο αριθμό αντικειμένων, καθώς τα κέρδη στην απόδοση αυξάνονται με την πολυπλοκότητα της σκηνής.
Προκλήσεις και Σκέψεις
Ενώ τα occlusion queries προσφέρουν σημαντικά οφέλη, υπάρχουν επίσης ορισμένες προκλήσεις και σκέψεις που πρέπει να ληφθούν υπόψη:
- Χρόνος Απόκρισης (Latency): Τα occlusion queries εισάγουν καθυστέρηση επειδή το αποτέλεσμα του query δεν είναι άμεσα διαθέσιμο. Η GPU χρειάζεται χρόνο για να επεξεργαστεί τις εντολές απόδοσης και να καθορίσει τον αριθμό των fragments που πέρασαν. Αυτή η καθυστέρηση μπορεί να οδηγήσει σε οπτικά σφάλματα (artifacts) αν δεν αντιμετωπιστεί προσεκτικά.
- Επιβάρυνση (Overhead) από τα Queries: Η εκτέλεση των occlusion queries συνεπάγεται επίσης ένα ορισμένο ποσό επιβάρυνσης. Η GPU πρέπει να παρακολουθεί την κατάσταση του query και να μετρά τα fragments που περνούν τον έλεγχο βάθους. Αυτή η επιβάρυνση μπορεί να αναιρέσει τα οφέλη στην απόδοση αν τα queries δεν χρησιμοποιούνται με σύνεση.
- Συντηρητική Απόκρυψη: Για να ελαχιστοποιηθεί η επίδραση της καθυστέρησης, είναι συχνά επιθυμητή η χρήση συντηρητικής απόκρυψης, όπου τα αντικείμενα θεωρούνται ορατά ακόμη και αν μόνο ένας μικρός αριθμός fragments είναι ορατός. Αυτό μπορεί να οδηγήσει στην απόδοση μερικώς αποκρυμμένων αντικειμένων, αλλά αποφεύγει τα οπτικά σφάλματα που μπορεί να προκύψουν με την επιθετική απόκρυψη.
- Επιλογή Bounding Volume: Η επιλογή του bounding volume (π.χ., bounding box, bounding sphere) για το occlusion query μπορεί να επηρεάσει σημαντικά την απόδοση. Τα απλούστερα bounding volumes αποδίδονται ταχύτερα αλλά μπορεί να οδηγήσουν σε περισσότερα ψευδώς θετικά αποτελέσματα (δηλαδή, αντικείμενα που θεωρούνται ορατά παρόλο που είναι ως επί το πλείστον αποκρυμμένα).
- Συγχρονισμός: Η λήψη του αποτελέσματος του query απαιτεί συγχρονισμό μεταξύ της CPU και της GPU. Αυτός ο συγχρονισμός μπορεί να προκαλέσει παύσεις (stalls) στο rendering pipeline, οι οποίες μπορούν να επηρεάσουν αρνητικά την απόδοση.
- Συμβατότητα Περιηγητή και Υλικού: Βεβαιωθείτε ότι οι στοχευμένοι περιηγητές και το υλικό υποστηρίζουν τα occlusion queries. Αν και υποστηρίζονται ευρέως, παλαιότερα συστήματα μπορεί να μην διαθέτουν αυτή τη λειτουργία, απαιτώντας μηχανισμούς εναλλακτικής λύσης (fallback).
Βέλτιστες Πρακτικές για τη Χρήση των WebGL Occlusion Queries
Για να μεγιστοποιήσετε τα οφέλη των occlusion queries και να ελαχιστοποιήσετε τις προκλήσεις, λάβετε υπόψη τις ακόλουθες βέλτιστες πρακτικές:
1. Χρησιμοποιήστε Απλοποιημένα Bounding Volumes
Αντί να αποδίδετε το πλήρες, λεπτομερές αντικείμενο για το occlusion query, αποδώστε ένα απλοποιημένο bounding volume, όπως ένα bounding box ή μια bounding sphere. Αυτό μειώνει τον φόρτο εργασίας απόδοσης και επιταχύνει τη διαδικασία του query. Το bounding volume πρέπει να περικλείει στενά το αντικείμενο για να ελαχιστοποιηθούν τα ψευδώς θετικά αποτελέσματα.
Παράδειγμα: Φανταστείτε ένα πολύπλοκο τρισδιάστατο μοντέλο ενός αυτοκινήτου. Αντί να αποδώσετε ολόκληρο το μοντέλο του αυτοκινήτου για το occlusion query, θα μπορούσατε να αποδώσετε ένα απλό bounding box που περικλείει το αυτοκίνητο. Αυτό το bounding box θα αποδοθεί πολύ γρηγορότερα από το πλήρες μοντέλο του αυτοκινήτου.
2. Χρησιμοποιήστε Ιεραρχικό Occlusion Culling
Για πολύπλοκες σκηνές, εξετάστε τη χρήση ιεραρχικού occlusion culling, όπου οργανώνετε τα αντικείμενα σε μια ιεραρχία από bounding volumes. Μπορείτε στη συνέχεια να εκτελέσετε occlusion queries πρώτα στα bounding volumes ανώτερου επιπέδου. Αν ένα bounding volume ανώτερου επιπέδου είναι αποκρυμμένο, μπορείτε να αποφύγετε την εκτέλεση occlusion queries στα παιδιά του. Αυτό μπορεί να μειώσει σημαντικά τον αριθμό των απαιτούμενων occlusion queries.
Παράδειγμα: Σκεφτείτε μια σκηνή με μια πόλη. Θα μπορούσατε να οργανώσετε τα κτίρια σε οικοδομικά τετράγωνα, και στη συνέχεια να οργανώσετε τα τετράγωνα σε περιοχές. Θα μπορούσατε έπειτα να εκτελέσετε occlusion queries πρώτα στις περιοχές. Αν μια περιοχή είναι αποκρυμμένη, μπορείτε να αποφύγετε την εκτέλεση occlusion queries στα επιμέρους τετράγωνα και κτίρια εντός αυτής της περιοχής.
3. Χρησιμοποιήστε τη Συνοχή μεταξύ Καρέ (Frame Coherency)
Τα occlusion queries παρουσιάζουν συνοχή μεταξύ καρέ (frame coherency), που σημαίνει ότι η ορατότητα ενός αντικειμένου είναι πιθανό να είναι παρόμοια από το ένα καρέ στο επόμενο. Μπορείτε να εκμεταλλευτείτε αυτή τη συνοχή αποθηκεύοντας προσωρινά (caching) τα αποτελέσματα των queries και χρησιμοποιώντας τα για να προβλέψετε την ορατότητα των αντικειμένων στα επόμενα καρέ. Αυτό μπορεί να μειώσει τον αριθμό των απαιτούμενων occlusion queries και να βελτιώσει την απόδοση.
Παράδειγμα: Αν ένα αντικείμενο ήταν ορατό στο προηγούμενο καρέ, μπορείτε να υποθέσετε ότι είναι πιθανό να είναι ορατό και στο τρέχον καρέ. Μπορείτε στη συνέχεια να καθυστερήσετε την εκτέλεση ενός occlusion query σε αυτό το αντικείμενο μέχρι να είναι πιθανό να αποκρυφτεί (π.χ., αν κινηθεί πίσω από ένα άλλο αντικείμενο).
4. Εξετάστε τη Χρήση Συντηρητικής Απόκρυψης
Για να ελαχιστοποιήσετε την επίδραση της καθυστέρησης, εξετάστε τη χρήση συντηρητικής απόκρυψης, όπου τα αντικείμενα θεωρούνται ορατά ακόμη και αν μόνο ένας μικρός αριθμός fragments είναι ορατός. Αυτό μπορεί να επιτευχθεί θέτοντας ένα κατώφλι (threshold) στο αποτέλεσμα του query. Αν το αποτέλεσμα του query είναι πάνω από το κατώφλι, το αντικείμενο θεωρείται ορατό. Διαφορετικά, θεωρείται αποκρυμμένο.
Παράδειγμα: Θα μπορούσατε να ορίσετε ένα κατώφλι 10 fragments. Αν το αποτέλεσμα του query είναι μεγαλύτερο από 10, το αντικείμενο θεωρείται ορατό. Διαφορετικά, θεωρείται αποκρυμμένο. Το κατάλληλο κατώφλι θα εξαρτηθεί από το μέγεθος και την πολυπλοκότητα των αντικειμένων στη σκηνή σας.
5. Εφαρμόστε έναν Μηχανισμό Εναλλακτικής Λύσης (Fallback)
Δεν υποστηρίζουν όλοι οι περιηγητές και το υλικό τα occlusion queries. Είναι σημαντικό να εφαρμόσετε έναν μηχανισμό εναλλακτικής λύσης (fallback) που μπορεί να χρησιμοποιηθεί όταν τα occlusion queries δεν είναι διαθέσιμα. Αυτό θα μπορούσε να περιλαμβάνει τη χρήση ενός απλούστερου αλγορίθμου occlusion culling ή απλώς την πλήρη απενεργοποίηση του occlusion culling.
Παράδειγμα: Θα μπορούσατε να ελέγξετε αν υποστηρίζεται η επέκταση EXT_occlusion_query_boolean. Αν όχι, θα μπορούσατε να καταφύγετε στη χρήση ενός απλού αλγορίθμου απόκρυψης βάσει απόστασης, όπου τα αντικείμενα που είναι πολύ μακριά από την κάμερα δεν αποδίδονται.
6. Βελτιστοποιήστε το Rendering Pipeline
Τα occlusion queries είναι μόνο ένα κομμάτι του παζλ όσον αφορά τη βελτιστοποίηση της απόδοσης. Είναι επίσης σημαντικό να βελτιστοποιήσετε και το υπόλοιπο rendering pipeline, συμπεριλαμβανομένων των εξής:
- Μείωση του αριθμού των draw calls: Η ομαδοποίηση των draw calls (batching) μπορεί να μειώσει σημαντικά την επιβάρυνση της απόδοσης.
- Χρήση αποδοτικών shaders: Η βελτιστοποίηση των shaders μπορεί να μειώσει τον χρόνο που δαπανάται για την επεξεργασία κάθε κορυφής (vertex) και τμήματος (fragment).
- Χρήση mipmapping: Το mipmapping μπορεί να βελτιώσει την απόδοση του φιλτραρίσματος των υφών (texture filtering).
- Μείωση του overdraw: Το overdraw συμβαίνει όταν τα fragments σχεδιάζονται το ένα πάνω στο άλλο, σπαταλώντας χρόνο επεξεργασίας.
- Χρήση instancing: Το instancing σας επιτρέπει να αποδώσετε πολλαπλά αντίγραφα του ίδιου αντικειμένου με μία μόνο κλήση σχεδίασης (draw call).
7. Ασύγχρονη Λήψη του Query
Η λήψη του αποτελέσματος του query μπορεί να προκαλέσει παύσεις (stalls) εάν η GPU δεν έχει ολοκληρώσει την επεξεργασία του query. Η χρήση μηχανισμών ασύγχρονης λήψης, εάν είναι διαθέσιμοι, μπορεί να βοηθήσει στον μετριασμό αυτού του προβλήματος. Οι τεχνικές μπορεί να περιλαμβάνουν την αναμονή για έναν ορισμένο αριθμό καρέ πριν από τη λήψη του αποτελέσματος ή τη χρήση αποκλειστικών worker threads για τη διαχείριση της διαδικασίας λήψης του query, αποτρέποντας το μπλοκάρισμα του κύριου νήματος απόδοσης (main rendering thread).
Παράδειγμα Κώδικα: Μια Βασική Υλοποίηση Occlusion Query
Ακολουθεί ένα απλοποιημένο παράδειγμα που επιδεικνύει τη βασική χρήση των occlusion queries στο WebGL:
// Δημιουργία ενός αντικειμένου query
const query = gl.createQuery();
// Έναρξη του query
gl.beginQuery(gl.ANY_SAMPLES_PASSED, query);
// Απόδοση του αντικειμένου (π.χ., ένα bounding box)
gl.drawArrays(gl.TRIANGLES, 0, vertexCount);
// Τερματισμός του query
gl.endQuery(gl.ANY_SAMPLES_PASSED);
// Ασύγχρονη λήψη του αποτελέσματος του query (παράδειγμα με requestAnimationFrame)
function checkQueryResult() {
gl.getQueryParameter(query, gl.QUERY_RESULT_AVAILABLE, (available) => {
if (available) {
gl.getQueryParameter(query, gl.QUERY_RESULT, (result) => {
const isVisible = result > 0;
// Χρήση του αποτελέσματος ορατότητας για την απόφαση απόδοσης του πλήρους αντικειμένου
if (isVisible) {
renderFullObject();
}
});
} else {
requestAnimationFrame(checkQueryResult);
}
});
}
requestAnimationFrame(checkQueryResult);
Σημείωση: Αυτό είναι ένα απλοποιημένο παράδειγμα και δεν περιλαμβάνει διαχείριση σφαλμάτων, σωστή διαχείριση πόρων ή προηγμένες τεχνικές βελτιστοποίησης. Θυμηθείτε να το προσαρμόσετε στη δική σας σκηνή και τις απαιτήσεις σας. Η διαχείριση σφαλμάτων, ειδικά όσον αφορά την υποστήριξη επεκτάσεων και τη διαθεσιμότητα των queries, είναι ζωτικής σημασίας σε περιβάλλοντα παραγωγής. Πρέπει επίσης να ληφθούν υπόψη προσαρμογές για τη διαχείριση διαφορετικών πιθανών σεναρίων.
Τα Occlusion Queries σε Εφαρμογές του Πραγματικού Κόσμου
Τα occlusion queries χρησιμοποιούνται σε ένα ευρύ φάσμα πραγματικών εφαρμογών, όπως:
- Ανάπτυξη Παιχνιδιών: Το occlusion culling είναι μια κρίσιμη τεχνική για τη βελτιστοποίηση της απόδοσης σε παιχνίδια, ιδιαίτερα σε πολύπλοκες σκηνές με πολλά αντικείμενα. Παραδείγματα περιλαμβάνουν τίτλους AAA που αποδίδονται σε browser χρησιμοποιώντας WebAssembly και WebGL, καθώς και casual παιχνίδια βασισμένα στο web με λεπτομερή περιβάλλοντα.
- Αρχιτεκτονική Οπτικοποίηση: Τα occlusion queries μπορούν να χρησιμοποιηθούν για τη βελτίωση της απόδοσης των αρχιτεκτονικών οπτικοποιήσεων, επιτρέποντας στους χρήστες να εξερευνούν μεγάλα και λεπτομερή μοντέλα κτιρίων σε πραγματικό χρόνο. Φανταστείτε την εξερεύνηση ενός εικονικού μουσείου με αμέτρητα εκθέματα - το occlusion culling εξασφαλίζει ομαλή πλοήγηση.
- Συστήματα Γεωγραφικών Πληροφοριών (GIS): Τα occlusion queries μπορούν να χρησιμοποιηθούν για τη βελτιστοποίηση της απόδοσης μεγάλων και πολύπλοκων γεωγραφικών συνόλων δεδομένων, όπως πόλεις και τοπία. Για παράδειγμα, η οπτικοποίηση τρισδιάστατων μοντέλων αστικών τοπίων μέσα σε έναν web browser για προσομοιώσεις πολεοδομικού σχεδιασμού μπορεί να ωφεληθεί σημαντικά από το occlusion culling.
- Ιατρική Απεικόνιση: Τα occlusion queries μπορούν να χρησιμοποιηθούν για τη βελτίωση της απόδοσης εφαρμογών ιατρικής απεικόνισης, επιτρέποντας στους γιατρούς να οπτικοποιούν πολύπλοκες ανατομικές δομές σε πραγματικό χρόνο.
- Ηλεκτρονικό Εμπόριο: Για ιστότοπους που παρουσιάζουν τρισδιάστατα μοντέλα προϊόντων, τα occlusion queries μπορούν να βοηθήσουν στη μείωση του φόρτου της GPU, εξασφαλίζοντας μια πιο ομαλή εμπειρία ακόμη και σε λιγότερο ισχυρές συσκευές. Σκεφτείτε την προβολή ενός τρισδιάστατου μοντέλου ενός πολύπλοκου επίπλου σε μια φορητή συσκευή· το occlusion culling μπορεί να βοηθήσει στη διατήρηση ενός λογικού frame rate.
Συμπέρασμα
Τα WebGL occlusion queries είναι ένα ισχυρό εργαλείο για τη βελτιστοποίηση της απόδοσης και τη βελτίωση της εμπειρίας χρήστη σε web εφαρμογές. Με την αποτελεσματική απόκρυψη των επικαλυπτόμενων αντικειμένων, μπορείτε να μειώσετε τον φόρτο εργασίας απόδοσης, να βελτιώσετε τα frame rates και να επιτρέψετε πιο πολύπλοκες και λεπτομερείς σκηνές. Αν και υπάρχουν προκλήσεις που πρέπει να ληφθούν υπόψη, όπως η καθυστέρηση και η επιβάρυνση των queries, η τήρηση των βέλτιστων πρακτικών και η προσεκτική εξέταση των συγκεκριμένων αναγκών της εφαρμογής σας μπορεί να απελευθερώσει το πλήρες δυναμικό των occlusion queries. Κατακτώντας αυτές τις τεχνικές, οι προγραμματιστές παγκοσμίως μπορούν να προσφέρουν πλουσιότερες, πιο καθηλωτικές και αποδοτικές τρισδιάστατες εμπειρίες βασισμένες στο web.
Περαιτέρω Πηγές
- Προδιαγραφή WebGL: Ανατρέξτε στην επίσημη προδιαγραφή WebGL για τις πιο ενημερωμένες πληροφορίες σχετικά με τα occlusion queries.
- Khronos Group: Εξερευνήστε τον ιστότοπο του Khronos Group για πηγές σχετικές με το WebGL και το OpenGL ES.
- Διαδικτυακά Εκπαιδευτικά Υλικά και Άρθρα: Αναζητήστε διαδικτυακά εκπαιδευτικά υλικά και άρθρα για τα WebGL occlusion queries για πρακτικά παραδείγματα και προηγμένες τεχνικές.
- Demos WebGL: Εξετάστε υπάρχοντα demos WebGL που χρησιμοποιούν occlusion queries για να μάθετε από πραγματικές υλοποιήσεις.